package asm.dump;

import org.ricks.asm.*;
import org.ricks.common.Tuple;

import java.util.List;


/**
 * @author chenwei
 * @Description:ASM 构建指令调用管理
 * @date 2022/9/1515:01
 */
public class DispacherDump implements Opcodes {

    public static ClassWriter dump(List<Tuple> tupleList) throws Exception {
//ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS
        ClassWriter classWriter = new ClassWriter(0);
        MethodVisitor methodVisitor;
        classWriter.visit(V17, ACC_PUBLIC | ACC_SUPER, "org/ricks/dispatch/Dispatcher", null, "java/lang/Object", null);
        classWriter.visitSource("Dispatcher.java", null);
        classWriter.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", "java/lang/invoke/MethodHandles", "Lookup", ACC_PUBLIC | ACC_FINAL | ACC_STATIC);

        {
            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(15, label0);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            methodVisitor.visitInsn(RETURN);
            Label label1 = new Label();
            methodVisitor.visitLabel(label1);
            methodVisitor.visitLocalVariable("this", "Lorg/demon/dispatch/Dispatcher;", null, label0, label1, 0);
            methodVisitor.visitMaxs(1, 1);
            methodVisitor.visitEnd();
        }
        {
            methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC , "dispatcher", "(Lorg/demon/common/Context;)V", "(Lorg/demon/common/Context<Ljava/lang/Short;[Ljava/lang/Byte;>;)V", null);
            methodVisitor.visitParameter("context", 0);
            methodVisitor.visitCode();

//            Label a = new Label();
//            methodVisitor.visitLabel(a);
//            methodVisitor.visitLineNumber(22, a);
//            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;");
//            methodVisitor.visitLdcInsn("\u8fdb\u6765\u4e86\u3002\u3002\u3002\u3002\u3002\u3002");
//            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(40, label0);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "org/ricks/common/Context", "getCmd", "()Ljava/lang/Object;", false);
            methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Short");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
            int[] messageIds = new int[tupleList.size()];
            Label[] labels = new Label[tupleList.size()];
            for (int i = 0; i < tupleList.size(); i++) {
                labels[i] = new Label();
                messageIds[i] = Integer.valueOf(tupleList.get(i).get(0).toString());
            }
            Label defaultLabel  = new Label();
            Label gotoLabel  = new Label();
            methodVisitor.visitLookupSwitchInsn(defaultLabel, messageIds, labels);
            int currLineNum = 40;
            for (int i = 0; i < tupleList.size(); i++) {
                currLineNum += 3;
                createMethodFunction(methodVisitor,tupleList.get(i), labels[i],currLineNum,gotoLabel);
            }
            methodVisitor.visitLabel(defaultLabel);
            methodVisitor.visitLineNumber(currLineNum + 3, defaultLabel);
            methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;");
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "org/ricks/common/Context", "getCmd", "()Ljava/lang/Object;", false);
            methodVisitor.visitInvokeDynamicInsn("makeConcatWithConstants", "(Ljava/lang/Object;)Ljava/lang/String;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/StringConcatFactory", "makeConcatWithConstants", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", false), new Object[]{"\u0001 \u672a\u77e5\u6307\u4ee4"});
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            methodVisitor.visitLabel(gotoLabel);
            methodVisitor.visitLineNumber(currLineNum + 6, gotoLabel);
            methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
            methodVisitor.visitInsn(RETURN);
            Label label10 = new Label();
            methodVisitor.visitLabel(label10);
            methodVisitor.visitLocalVariable("context", "Lorg/demon/common/Context;", "Lorg/demon/common/Context<Ljava/lang/Short;[Ljava/lang/Byte;>;", label0, label10, 0);
            methodVisitor.visitMaxs(2, 1);
            methodVisitor.visitEnd();
        }
        classWriter.visitEnd();
        return classWriter;
    }

    private static void  createMethodFunction(MethodVisitor methodVisitor,Tuple tuple, Label label,int lineNum,Label gotoLabel) {
        System.err.println(" tuple1:"+ tuple.get(1)  +" and tuple2:" + tuple.get(2));
        methodVisitor.visitLabel(label);
        methodVisitor.visitLineNumber(lineNum, label);
        methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        methodVisitor.visitLdcInsn(Type.getType("L"+tuple.get(1)+";"));
        methodVisitor.visitMethodInsn(INVOKESTATIC, "org/ricks/ioc/ApplicationContext", "getBean", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
        methodVisitor.visitTypeInsn(CHECKCAST, tuple.get(1));
        methodVisitor.visitVarInsn(ALOAD, 0);
        methodVisitor.visitMethodInsn(INVOKEVIRTUAL, tuple.get(1), tuple.get(2), "(Lorg/demon/common/Context;)V", false);

        Label line = new Label();
        methodVisitor.visitLabel(line);
        methodVisitor.visitLineNumber(lineNum + 1,line);
        methodVisitor.visitJumpInsn(GOTO, gotoLabel);
    }
}